package com.share.device.emqx.handler.Impl;

import com.alibaba.fastjson2.JSONObject;
import com.share.common.core.utils.SnowflakeIdGenerator;
import com.share.common.core.utils.StringUtils;
import com.share.common.rabbit.MqConstant.MqConst;
import com.share.common.rabbit.MqSender.Sender;
import com.share.device.emqx.annotation.GuiguEmqx;
import com.share.device.emqx.constant.EmqxConstants;
import com.share.device.emqx.handler.MassageHandler;
import com.share.device.service.ICabinetService;
import com.share.device.service.ICabinetSlotService;
import com.share.device.service.IPowerBankService;
import com.share.device.service.IStationService;
import com.share.order.domain.SubmitOrderVo;
import com.share.system.api.domain.Cabinet;
import com.share.system.api.domain.CabinetSlot;
import com.share.system.api.domain.PowerBank;
import com.share.system.api.domain.Station;
import lombok.extern.slf4j.Slf4j;
import org.redisson.api.RLock;
import org.redisson.api.RedissonClient;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.stereotype.Component;
import org.springframework.transaction.annotation.Transactional;

import java.util.Date;
import java.util.concurrent.TimeUnit;

/**
 * 充电宝弹出消息处理类
 * 柜机进行弹出向后台发送消息，这个是充电宝弹出消息的接收类
 */
@Slf4j
@Component
@GuiguEmqx(topic = EmqxConstants.TOPIC_POWERBANK_UNLOCK)
public class PowerBankUnlockHandler implements MassageHandler {

    @Autowired
    private ICabinetService cabinetService;

    @Autowired
    private IPowerBankService powerBankService;

    @Autowired
    private ICabinetSlotService cabinetSlotService;

    @Autowired
    private IStationService stationService;

    @Autowired
    private RedisTemplate redisTemplate;

    @Autowired
    private RedissonClient redissonClient;

    @Autowired
    private Sender sender;

    @Autowired
    private SnowflakeIdGenerator idGenerator;

    @Transactional(rollbackFor = Exception.class)
    @Override
    public void handleMessage(JSONObject message) {
        //消息编号
        String messageNo = message.getString("mNo");
        //防止重复请求的二次检查
        String key = "powerBank:unlock:processed:" + messageNo;
        Boolean isExist = redisTemplate.opsForValue().setIfAbsent(key, "1", 1, TimeUnit.HOURS);
        if (Boolean.FALSE.equals(isExist)) {
            log.info("重复请求已处理: {}", message.toJSONString());
            return;
        }
        //获取锁(可重入)，指定锁的名称
        RLock lock = redissonClient.getLock("powerBank:unlock:" + messageNo);
        if (lock.tryLock()) {
            try {
                log.info("开始处理消息: {}", message.toJSONString());
                log.info("handleMessage: {}", message.toJSONString());
                /**
                 * 使用分布式锁，解决接口幂等性问题，接口重试以及网络抖动都可能导致重复调用
                 * 三种方案：数据库唯一约束，redis分布式锁，幂等token
                 * 使用分布式锁避免保证幂等性，避免重复调用，key使用消息id，或者使用消息id+用户id组合等等
                 */
                //柜机编号
                String cabinetNo = message.getString("cNo");
                //充电宝编号
                String powerBankNo = message.getString("pNo");
                //插槽编号
                String slotNo = message.getString("sNo");
                //用户id
                Long userId = message.getLong("uId");
                if (StringUtils.isEmpty(cabinetNo)
                        || StringUtils.isEmpty(powerBankNo)
                        || StringUtils.isEmpty(slotNo)
                        || null == userId) {
                    log.info("参数为空: {}", message.toJSONString());
                    return;
                }
                //获取柜机
                Cabinet cabinet = cabinetService.getBtCabinetNo(cabinetNo);
                // 获取充电宝
                PowerBank powerBank =powerBankService.getByPowerBankNo(powerBankNo);
                // 获取插槽
                CabinetSlot cabinetSlot = cabinetSlotService.getBtSlotNo(cabinet.getId(), slotNo);
                // 获取站点
                Station station = stationService.getByCabinetId(cabinet.getId());

                //更新充电宝状态
                // 状态（0:未投放 1：可用 2：已租用 3：充电中 4：故障）
                powerBank.setStatus("2");
                powerBankService.updateById(powerBank);

                //更新插槽状态
                // 状态（1：占用 0：空闲）
                cabinetSlot.setStatus("0");
                cabinetSlot.setPowerBankId(null);
                cabinetSlot.setUpdateTime(new Date());
                cabinetSlotService.updateById(cabinetSlot);

                //更新柜机信息
                int freeSlots = cabinet.getFreeSlots() + 1;
                cabinet.setFreeSlots(freeSlots);
                int usedSlots = cabinet.getUsedSlots() - 1;
                cabinet.setUsedSlots(usedSlots);
                //可以借用
                int availableNum = cabinet.getAvailableNum() - 1;
                cabinet.setAvailableNum(availableNum);
                cabinet.setUpdateTime(new Date());
                cabinetService.updateById(cabinet);

                //发送消息构建订单
                //TODO 异步生成订单
                //构建订单对象
                SubmitOrderVo submitOrderVo = new SubmitOrderVo();
                submitOrderVo.setMessageNo(messageNo);
                submitOrderVo.setUserId(userId);
                submitOrderVo.setPowerBankNo(powerBankNo);
                submitOrderVo.setStartStationId(station.getId());
                submitOrderVo.setStartStationName(station.getName());
                submitOrderVo.setStartCabinetNo(cabinetNo);
                submitOrderVo.setFeeRuleId(station.getFeeRuleId());
                log.info("构建订单对象: {}", JSONObject.toJSONString(submitOrderVo));

                //发送信息，创建订单，在充电宝归还之后再结束订单发起支付服务
                String snowflakeMessageId = String.valueOf(idGenerator.nextId());
                sender.sendCreateOrder(snowflakeMessageId, submitOrderVo);

            } finally {
                lock.unlock();
            }
        }else {
            log.info("检测到重复消息，拒绝处理: {}", message.toJSONString());
            return;
        }


    }

}
